#ifndef COM_H
#define COM_H

#include <windows.h>
#include <objbase.h>
#include <comdef.h>
#include <assert.h>
#include <ExDisp.h>

#ifdef _MSC_VER
#define	NOVTABLE __declspec(novtable)
#else
#define	NOVTABLE
#endif
#define	ANSUNC

#ifdef _MSC_VER
#pragma warning(disable: 4355)	// use of 'this' for initialization of _connector
#endif

#define	THROW_EXCEPTION(hr) throw _com_error(hr)
#define	CHECKERROR(hr) ((void)(FAILED(hr)? THROW_EXCEPTION(hr): 0))

 /// wrapper class for COM interface pointers

template<typename T> struct SIfacePtr
{
	SIfacePtr()
	 :	m_p(0)
	{
	}

	SIfacePtr(T* p)
	 :	m_p(p)
	{
		if (p)
			p->AddRef();
	}

	SIfacePtr(IUnknown* unknown, REFIID riid)
	{
		CHECKERROR(unknown->QueryInterface(riid, (LPVOID*)&m_p));
	}

	~SIfacePtr()
	{
		Free();
	}

	T* operator->()
	{
		return m_p;
	}

	const T* operator->() const
	{
		return m_p;
	}

	operator T*()
	{
		return m_p;
	}

	T** operator&()
	{
		return &m_p;
	}

	bool empty() const	//NOTE: GCC seems not to work correctly when defining operator bool() AND operator T*() at one time
	{
		return !m_p;
	}

	SIfacePtr& operator=(T* p)
	{
		Free();

		if (p) {
			p->AddRef();
			m_p = p;
		}

		return *this;
	}

	void operator=(SIfacePtr const& o)
	{
		T* h = m_p;

		if (o.m_p)
			o.m_p->AddRef();

		m_p = o.m_p;

		if (h)
			h->Release();
	}

	HRESULT CreateInstance(REFIID clsid, REFIID riid)
	{
		return CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, riid, (LPVOID*)&m_p);
	}

	template<typename I> HRESULT QueryInterface(REFIID riid, I* p)
	{
		return m_p->QueryInterface(riid, (LPVOID*)p);
	}

	T* get()
	{
		return m_p;
	}

	void Free()
	{
		T* h = m_p;
		m_p = NULL;

		if (h)
			h->Release();
	}

protected:
	SIfacePtr(const SIfacePtr& o)
	 :	m_p(o.m_p)
	{
		if (m_p)
			m_p->AddRef();
	}

	T* m_p;
};



struct NOVTABLE ComSrvObject	// NOVTABLE erlaubt, da protected Destruktor
{
protected:
	ComSrvObject() : m_ref(1) {}
	virtual ~ComSrvObject() {}

	ULONG	m_ref;
};

struct SimpleComObject : public ComSrvObject
{
	ULONG IncRef() {return ++m_ref;}
	ULONG DecRef() {ULONG ref=--m_ref; if (!ref) {m_ref++; delete this;} return ref;}
};


 // server object interfaces

template<typename BASE> struct IComSrvQI : public BASE
{
	IComSrvQI(REFIID uuid_base)
	 :	m_uuid_base(uuid_base)
	{
	}

	STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv) {*ppv=0;
	 if (IsEqualIID(riid, m_uuid_base) ||
		 IsEqualIID(riid, IID_IUnknown)) {*ppv=static_cast<BASE*>(this); this->AddRef(); return S_OK;}
	 return E_NOINTERFACE;}

protected:
	IComSrvQI() {}
	
	IComSrvQI& operator=(const IComSrvQI& rhs);	
	IComSrvQI(const IComSrvQI& rhs);

	REFIID	m_uuid_base;
};

template<> struct IComSrvQI<IUnknown> : public IUnknown
{
	STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv) {*ppv=0;
	 if (IsEqualIID(riid, IID_IUnknown)) {*ppv=this; AddRef(); return S_OK;}
	 return E_NOINTERFACE;}

protected:
	IComSrvQI<IUnknown>() {}
};


template<typename BASE, typename OBJ>
	class IComSrvBase : public IComSrvQI<BASE>
{
	typedef IComSrvQI<BASE> super;

protected:
	IComSrvBase(REFIID uuid_base)
	 :	super(uuid_base)
	{
	}

public:
	STDMETHODIMP_(ULONG) AddRef() {return static_cast<OBJ*>(this)->IncRef();}
	STDMETHODIMP_(ULONG) Release() {return static_cast<OBJ*>(this)->DecRef();}
};


template<typename T> struct ConnectionPoint : public SIfacePtr<T>
{
	ConnectionPoint(IConnectionPointContainer* pCPC, REFIID riid)
	{
		CHECKERROR(pCPC->FindConnectionPoint(riid, &this->m_p));
	}
};

struct EventConnection
{
	EventConnection(IConnectionPoint* connectionpoint, IUnknown* sink)
	{
		CHECKERROR(connectionpoint->Advise(sink, &_cookie));
		_connectionpoint = connectionpoint;
	}

	template<typename T> EventConnection(T& connectionpoint, IUnknown* sink)
	{
		CHECKERROR(connectionpoint->Advise(sink, &_cookie));
		_connectionpoint = connectionpoint;
	}

	~EventConnection()
	{
		if (_connectionpoint)
			_connectionpoint->Unadvise(_cookie);
	}

protected:
	SIfacePtr<IConnectionPoint> _connectionpoint;
	DWORD	_cookie;
};

struct EventConnector : public EventConnection
{
	EventConnector(IUnknown* unknown, REFIID riid, IUnknown* sink)
	 :	EventConnection(ConnectionPoint<IConnectionPoint>(
				SIfacePtr<IConnectionPointContainer>(unknown, IID_IConnectionPointContainer), riid), sink)
	{
	}
};


struct OleInPlaceClient : public SimpleComObject,
							public IOleClientSite,
							public IOleInPlaceSite
{
protected:
	HWND	m_hwnd;

public:
	OleInPlaceClient(HWND hwnd=0)
	 :	m_hwnd(hwnd)
	{
	}

	void attach(HWND hwnd)
	{
		m_hwnd = hwnd;
	}

	HRESULT attach_control(IOleObject* ole_obj, LONG iVerb=OLEIVERB_INPLACEACTIVATE, HWND hwndParent=0, LPCRECT pRect=NULL)
	{
		HRESULT hr = ole_obj->SetClientSite(this);
		if (FAILED(hr))
			return hr;

		hr = ole_obj->DoVerb(iVerb, NULL, this, 0, 0/*hwnd*/, NULL/*&rcPos*/);

		return hr;
	}

	HRESULT detach(IOleObject* ole_obj, DWORD dwSaveOption=OLECLOSE_SAVEIFDIRTY)
	{
		HRESULT hr = ole_obj->Close(dwSaveOption);

		m_hwnd = 0;

		return hr;
	}

	STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
	{
		if (IsEqualIID(riid, IID_IOleClientSite))
			{*ppv=static_cast<IOleClientSite*>(this); IncRef(); return S_OK;}

		if (IsEqualIID(riid, IID_IOleInPlaceSite))
			{*ppv=static_cast<IOleInPlaceSite*>(this); IncRef(); return S_OK;}

		if (IsEqualIID(riid, IID_IUnknown))
			{*ppv=static_cast<IOleClientSite/*oder auch IOleInPlaceSite*/*>(this); IncRef(); return S_OK;}

		return E_NOINTERFACE;
	}

	STDMETHODIMP_(ULONG) AddRef() {return IncRef();}
	STDMETHODIMP_(ULONG) Release() {return DecRef();}


	 // IOleWindow:

	virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow(/* [out] */ HWND __RPC_FAR *phwnd)
	{
		*phwnd = m_hwnd;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(/* [in] */ BOOL fEnterMode)
	{
		return E_NOTIMPL;
	}


	 // IOleClientSite:

	virtual HRESULT STDMETHODCALLTYPE SaveObject()
	{
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker __RPC_FAR *__RPC_FAR *ppmk)
	{
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetContainer(IOleContainer __RPC_FAR *__RPC_FAR *ppContainer)
	{
		ppContainer = 0;
		return E_NOINTERFACE;
	}

	virtual HRESULT STDMETHODCALLTYPE ShowObject()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow)
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout()
	{
		return S_OK;
	}


	 // IOleInPlaceSite:

	virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OnUIActivate()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE GetWindowContext(
		/* [out] */ IOleInPlaceFrame __RPC_FAR *__RPC_FAR *ppFrame,
		/* [out] */ IOleInPlaceUIWindow __RPC_FAR *__RPC_FAR *ppDoc,
		/* [out] */ LPRECT lprcPosRect,
		/* [out] */ LPRECT lprcClipRect,
		/* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo)
	{
		RECT r;
		GetClientRect(m_hwnd, &r);

		ppFrame = 0;
		ppDoc = 0;
		CopyRect(lprcPosRect, &r);
		CopyRect(lprcClipRect, &r);

		assert(lpFrameInfo->cb>=sizeof(OLEINPLACEFRAMEINFO));
		lpFrameInfo->fMDIApp = FALSE;
		lpFrameInfo->hwndFrame = 0;
		lpFrameInfo->haccel = 0;
		lpFrameInfo->cAccelEntries = 0;

		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE Scroll(/* [in] */ SIZE scrollExtant)
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate(/* [in] */ BOOL fUndoable)
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE DiscardUndoState()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo()
	{
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OnPosRectChange(/* [in] */ LPCRECT lprcPosRect)
	{
		return S_OK;
	}
};

#endif